﻿using System.Collections.Generic;
using Godot.core.os;

namespace Godot.core.io
{
    public partial class PackedData
    {
        public class PackedFile
        {
            public string pack;
            public ulong offset;
            public ulong size;
            public uint[] md5;
            public PackSource src;

            public PackedFile()
            {
                md5 = new uint[16];
            }
        }

        private static PackedData __singleton;

        public static PackedData singleton
        {
            get { return __singleton; }
        }

        public bool is_disabled()
        {
            return __disabled;
        }

        public FileAccess try_open_path(string p_path)
        {
            PackedFile E = null;

            if (p_path == null) return null;

            __files.TryGetValue(p_path, out E);
            
            if(E == null) return null;
            
            if(E.offset == 0) return null;
            
            return E.src.get_file(p_path, ref E);
        }

        public void add_path(string pkg_path, string path, ulong ofs, ulong size, byte[] p_md5, PackSource p_src) 
        {
	        bool exists = __files.ContainsKey(path);

	        PackedFile pf = new PackedFile();
	        pf.pack = pkg_path;
	        pf.offset = ofs;
	        pf.size = size;
            p_md5.CopyTo(pf.md5, 0);
	        pf.src = p_src;

	        __files[path] = pf;

	        if (!exists) 
            {
		        //search for dir
		        string p = path.Replace("res://","");
		        __PackedDir cd = __root;

		        if (p.IndexOf("/") != -1) //in a subdir
                { 
			        string[] ds = p.get_base_dir().Split(new char[] { '/' });

			        for(int j = 0; j < ds.Length; j++) 
                    {
				        if (cd.subdirs.ContainsKey(ds[j])) 
                        {
					        __PackedDir pd = new __PackedDir();
					        pd.name = ds[j];
					        pd.parent = cd;
					        cd.subdirs[pd.name] = pd;
					        cd = pd;
				        } 
                        else 
                        {
					        cd = cd.subdirs[ds[j]];
				        }
			        }
		        }
		        cd.files.Add(path.get_file());
	        }
        }

        public bool has_path(string p_name)
        {
            return __files.ContainsKey(p_name);
        }

        public Error add_pack(string p_path) 
        {
	        for (int i = 0; i < __sources.Count; i++) {

		        if (__sources[i].try_open_pack(p_path)) 
                {
			        return Error.OK;
		        };
	        };

	        return Error.ERR_FILE_UNRECOGNIZED;
        }

        public void add_pack_source(PackSource p_source) 
        {
	        __sources.Add(p_source);
        }

        public PackedData()
        {
            __singleton = this;
            __files = new Dictionary<string, PackedFile>();
            __sources = new List<PackSource>();
            __root = new __PackedDir();
            __root.parent = null;
            __disabled = false;
            add_pack_source(new PackSourcePCK());
        }

        private __PackedDir __root;
        private Dictionary<string, PackedFile> __files;
        private List<PackSource> __sources; 
        private bool __disabled;
    }

    public partial class PackedData
    {
        private class __PackedDir
        {
            public __PackedDir parent;
            public string name;
            public Dictionary<string, __PackedDir> subdirs;
            public SortedSet<string> files;

            public __PackedDir()
            {
                subdirs = new Dictionary<string, __PackedDir>();
                files = new SortedSet<string>();
            }
        };
    }
}
